using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project09
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;
        InputListener inputListener;

        private Texture2D xAxis, yAxis;
        private Vector2 rootCoordinate;
        private string infoString;
        private Vector2 infoPosition;

        private Object ball1, ball2;
        private float e;
        private float j;
        private int count;
        private float toi;

        private float timer;
        private bool isStart;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            this.IsMouseVisible = true;
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            inputListener = new InputListener(this);
            Components.Add(inputListener);

            xAxis = CreateRectangle(900, 1, Color.White);
            yAxis = CreateRectangle(720, 1, Color.White);
            rootCoordinate = new Vector2(450, 450);
            infoPosition = new Vector2(950, 50);

            //Physics data
            e = 0.0f;
            j = 0.0f;
            count = 0;
            toi = 0.0f;

            timer = 0.0f;
            isStart = false;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            spriteFont = Content.Load<SpriteFont>("gameFont");

            // TODO: use this.Content to load your game content here
            Texture2D ballTexture1 = Content.Load<Texture2D>("ball1");
            Texture2D ballTexture2 = Content.Load<Texture2D>("ball2");

            ball1 = new Object(ballTexture1, -380.0f, 1.0f, 100.0f);
            ball2 = new Object(ballTexture2, 80.0f, 1.0f, 0.0f);
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (inputListener.isKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            if (inputListener.isKeyPressed(Keys.Space))
                isStart = !isStart;
            if (inputListener.isKeyPressed(Keys.Back))
            {
                Initialize();
            }

            if (!isStart && timer == 0.0f)
            {
                ControlData();
                toi = (ball1.Position - ball2.Position + 40) / (ball2.InitialVelocity - ball1.InitialVelocity);
            }

            if (isStart)
            {
                timer += (float)gameTime.ElapsedGameTime.TotalSeconds;

                if (timer < toi)
                {
                    ball1.Position += (float)(ball1.InitialVelocity * gameTime.ElapsedGameTime.TotalSeconds);
                    ball2.Position += (float)(ball2.InitialVelocity * gameTime.ElapsedGameTime.TotalSeconds);
                }
                else
                {
                    count = 1;

                    j = (-(ball1.InitialVelocity - ball2.InitialVelocity) * (e + 1) * ball1.Mass * ball2.Mass) / (ball1.Mass + ball2.Mass);
                    ball1.FinalVelocity = j / ball1.Mass + ball1.InitialVelocity;
                    ball2.FinalVelocity = (-j / ball2.Mass) + ball2.InitialVelocity;

                    ball1.Position += (float)(ball1.FinalVelocity * gameTime.ElapsedGameTime.TotalSeconds);
                    ball2.Position += (float)(ball2.FinalVelocity * gameTime.ElapsedGameTime.TotalSeconds);
                }
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            //Coordinate
            spriteBatch.Draw(xAxis, rootCoordinate, null, Color.White, 0f, new Vector2(rootCoordinate.X, 1), 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(yAxis, rootCoordinate, null, Color.White, MathHelper.PiOver2, new Vector2(rootCoordinate.Y, 1), 1f, SpriteEffects.None, 0f);

            //Objects
            ball1.Draw(spriteBatch, rootCoordinate);
            ball2.Draw(spriteBatch, rootCoordinate);

            //Info
            infoString = "M1: " + ball1.Mass + " kg    " + ball1.Position.ToString("0.0") + " m ";
            infoString += "\nM2: " + ball2.Mass + " kg    " + ball2.Position.ToString("0.0") + " m";
            infoString += "\n\nInitial: u = " + ball1.InitialVelocity.ToString("0.0") + " m/s    v = " + ball2.InitialVelocity.ToString("0.0") + " m/s";
            infoString += "\nFinal: u = " + ball1.FinalVelocity.ToString("0.0") + " m/s    v = " + ball2.FinalVelocity.ToString("0.0") + " m/s";
            infoString += "\n\n e = " + e.ToString("0.0");
            infoString += "\nImpulse J = " + j.ToString("0.0") + " kg m/s";
            infoString += "\nCollision Count = " + count;
            infoString += "\n\np_i = " + (ball1.Mass * ball1.InitialVelocity) + " + " + (ball2.Mass * ball2.InitialVelocity) + " = " + (ball1.Mass * ball1.InitialVelocity + ball2.Mass * ball2.InitialVelocity) + " kg m/s";
            infoString += "\np_f = " + (ball1.Mass * ball1.FinalVelocity) + " + " + (ball2.Mass * ball2.FinalVelocity) + " = " + (ball1.Mass * ball1.FinalVelocity + ball2.Mass * ball2.FinalVelocity) + " kg m/s"; ;
            infoString += "\n\nTimer: " + timer.ToString("0.00") + " s";
            spriteBatch.DrawString(spriteFont, infoString, infoPosition, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        private Texture2D CreateRectangle(int width, int height, Color objColor)
        {
            Texture2D rectTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            Color[] color = new Color[width * height];

            for (int i = 0; i < color.Length; i++)
                color[i] = objColor;

            rectTexture.SetData(color);

            return rectTexture;
        }

        private void ControlData()
        {
            if (inputListener.isKeyPressed(Keys.Q))
                ball1.Mass += 1.0f;
            if (inputListener.isKeyPressed(Keys.A))
                ball1.Mass -= 1.0f;
            ball1.Mass = MathHelper.Clamp(ball1.Mass, 1.0f, 10.0f);

            if (inputListener.isKeyPressed(Keys.I))
                ball2.Mass += 1.0f;
            if (inputListener.isKeyPressed(Keys.K))
                ball2.Mass -= 1.0f;
            ball2.Mass = MathHelper.Clamp(ball2.Mass, 1.0f, 10.0f);

            if (inputListener.isKeyPressed(Keys.W))
                ball1.InitialVelocity += 10.0f;
            if (inputListener.isKeyPressed(Keys.S))
                ball1.InitialVelocity -= 10.0f;
            ball1.InitialVelocity = MathHelper.Clamp(ball1.InitialVelocity, 50.0f, 150.0f);

            if (inputListener.isKeyPressed(Keys.O))
                ball2.InitialVelocity += 10.0f;
            if (inputListener.isKeyPressed(Keys.L))
                ball2.InitialVelocity -= 10.0f;
            ball2.InitialVelocity = MathHelper.Clamp(ball2.InitialVelocity, -50.0f, 10.0f);

            if (inputListener.isKeyPressed(Keys.F))
                e += 0.1f;
            if (inputListener.isKeyPressed(Keys.G))
                e -= 0.1f;
            e = MathHelper.Clamp(e, 0.0f, 2.0f);
        }
    }
}
